/*******************************************************************************
 * Copyright (c) 2010, 2018 西安秦晔信息科技有限公司
 * Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *******************************************************************************/
package com.qinyeit;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.collect.Sets;
import com.qinyeit.serviceapp.entity.system.ManagementAccount;
import com.qinyeit.serviceapp.entity.system.ManagementRole;
import com.qinyetech.springstage.core.repository.jdbc.PageNamedParameterJdbcTemplate;
import com.qinyetech.springstage.core.repository.support.BaseRepositoryFactoryBean;
import com.qinyetech.springstage.core.security.SimpleByteSource;
import com.qinyetech.springstage.core.security.StatelessSecurityToken;
import com.qinyetech.springstage.core.security.TerminalDevice;
import com.qinyetech.springstage.core.security.exception.BlankPasswordException;
import com.qinyetech.springstage.core.web.method.annotation.SearchableMethodArgumentResolver;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.filter.HttpPutFormContentFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.sql.DataSource;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.qinyeit.serviceapp.security.PrincipalIdentifierKey.*;
import static com.qinyeit.serviceapp.security.PrincipalIdentifierKey.PRINCIPAL_ACCOUNT_NAME;
import static com.qinyeit.serviceapp.security.PrincipalIdentifierKey.PRINCIPAL_SECURITY_KEY;

/**
 * <p>ClassName: com.qinyeit.TestApplicationConfiguration
 * <p>Function: 配置
 * <p>date: 2018-08-20 15:48
 *
 * @author wuqing
 * @version 1.0
 * @since JDK 1.8
 */
@Slf4j
@EnableJpaRepositories(repositoryImplementationPostfix="Impl",repositoryFactoryBeanClass=BaseRepositoryFactoryBean.class)
public class BaseConfiguration implements WebMvcConfigurer {
    /**
     *  SpringMVC在序列化json的时候，如果对象中包含java.lang.Object对象会出错，这个配置可以消除这个错误
     * @return
     */
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        return mapper;
    }
    /**
     * 参数解析器
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new SearchableMethodArgumentResolver());
    }

    @Bean
    public Realm realm() {
        final String accountName="administrator";
        final String password="123456";
        TextConfigurationRealm realm = new TextConfigurationRealm(){
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

                Map principalMap=new HashedMap();
                principalMap.put(PRINCIPAL_ACCOUNT_NAME,accountName);//账号名称
                principalMap.put(PRINCIPAL_ACCOUNT_ID,1L); //账号id
                principalMap.put(PRINCIPAL_IS_ADMIN,true);//是否是管理员
                principalMap.put(PRINCIPAL_TERMINAL_DEVICE, TerminalDevice.PCWEB); //平台类型，获取授权的时候使用
                principalMap.put(PRINCIPAL_LAST_LOGIN_AT,new Date());
                principalMap.put(PRINCIPAL_ACCOUNT_HEAD_URL,null);
                principalMap.put(PRINCIPAL_ACCOUNT_REAL_NAME,"管理员");
                String uuid= UUID.randomUUID().toString();
                //产生密钥
                String securityKey=new Md5Hash(uuid,accountName).toString();
                principalMap.put(PRINCIPAL_SECURITY_KEY,securityKey);
                // 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
                SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                        principalMap , // 用户名
                        password, // 密码
                        getName() // realm name
                );
                return authenticationInfo;
            }

            /**
             * 获取授权信息
             * @param principals
             * @return
             */
            @Override
            @SuppressWarnings("unchecked")
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
                SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                //指定角色字符串
                authorizationInfo.setRoles(Sets.newHashSet("admin"));
                authorizationInfo.setStringPermissions(Sets.newHashSet("*", "*:*", "*:*:*", "*:*:*:*"));  //超级管理员拥有所有权限
                return authorizationInfo;
            }
        };
        realm.setUserDefinitions(accountName+"="+password+",admin");
        realm.setRoleDefinitions("admin=*");
        return realm;

    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }
    
    @Bean(name="branchJdbcTemplate")
    public PageNamedParameterJdbcTemplate jdbcTemplate(@Qualifier("branchDataSource") DataSource dataSource){
        return new PageNamedParameterJdbcTemplate(dataSource);
    }

    @Bean(name = "primaryDataSource")
    @Primary
    public DataSource primaryDataSource(@Qualifier("primaryDataSourceConfig") AtomikosDataSourceProperties config) {
        return buildAtomikosDataSourceBean(buildDataSourceProperty(config),config.getName());
    }

    @Bean(name = "branchDataSource")
    public DataSource branchDataSource(@Qualifier("branchDataSourceConfig") AtomikosDataSourceProperties config) {
        return buildAtomikosDataSourceBean(buildDataSourceProperty(config),config.getName());
    }

    @Bean(name="primaryDataSourceConfig")
    @ConfigurationProperties("spring.datasource.primary")
    public AtomikosDataSourceProperties primaryDataSourceProperty(){
        return new AtomikosDataSourceProperties();
    }
    @Bean(name="branchDataSourceConfig")
    @ConfigurationProperties("spring.datasource.branch")
    public AtomikosDataSourceProperties branchDataSourceProperty(){
        return new AtomikosDataSourceProperties();
    }


    private AtomikosDataSourceBean buildAtomikosDataSourceBean(Properties properties, String name){
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        ds.setUniqueResourceName(name);
        ds.setPoolSize(5);
        ds.setXaProperties(properties);
        return ds;
    }
    private Properties buildDataSourceProperty(AtomikosDataSourceProperties atomikosDataSourceProperties){
        Properties prop = new Properties();
        prop.put("url", atomikosDataSourceProperties.getUrl());
        prop.put("username",atomikosDataSourceProperties.getUsername());
        prop.put("password", atomikosDataSourceProperties.getPassword());
        prop.put("driverClassName",atomikosDataSourceProperties.getDriverClassName());
        prop.put("initialSize",atomikosDataSourceProperties.getInitialSize());
        prop.put("maxActive",atomikosDataSourceProperties.getMaxActive());
        prop.put("minIdle", atomikosDataSourceProperties.getMinIdle());
        prop.put("maxWait", atomikosDataSourceProperties.getMaxWait());
        prop.put("poolPreparedStatements", atomikosDataSourceProperties.getPoolPreparedStatements());

        prop.put("maxPoolPreparedStatementPerConnectionSize",
                atomikosDataSourceProperties.getMaxPoolPreparedStatementPerConnectionSize());

        prop.put("validationQuery", atomikosDataSourceProperties.getValidationQuery());
        prop.put("testOnBorrow", atomikosDataSourceProperties.getTestOnBorrow());
        prop.put("testOnReturn",atomikosDataSourceProperties.getTestOnReturn());
        prop.put("testWhileIdle",atomikosDataSourceProperties.getTestWhileIdle());
        prop.put("timeBetweenEvictionRunsMillis",
                atomikosDataSourceProperties.getTimeBetweenEvictionRunsMillis());
        prop.put("minEvictableIdleTimeMillis", atomikosDataSourceProperties.getMinEvictableIdleTimeMillis());
        return prop;
    }


}
@Data
class AtomikosDataSourceProperties{
    private String name="";
    private String url="";
    private String username="";
    private String password="";
    private String driverClassName="";
    private Integer initialSize;
    private Integer maxActive;
    private Integer minIdle;
    private Long  maxWait;
    private Boolean poolPreparedStatements=true;
    private Integer maxPoolPreparedStatementPerConnectionSize;
    private String validationQuery="SELECT 'x'";
    private Boolean testOnBorrow=false;
    private Boolean testOnReturn=false;
    private Boolean testWhileIdle=true;
    private Long timeBetweenEvictionRunsMillis;
    private Long minEvictableIdleTimeMillis;

}